﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace Spk.Controls.Common
{
    public class UXTheme
    {
        // Private imports ---------------------------------------------------

        private class Native
        {
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int left, top, right, bottom;

                public RECT(Rectangle rectangle)
                {
                    left = rectangle.Left;
                    right = rectangle.Right;
                    top = rectangle.Top;
                    bottom = rectangle.Bottom;
                }

                public static RECT FromRectangle(Rectangle rectangle)
                {
                    return new RECT(rectangle);
                }

                public Rectangle ToRectangle()
                {
                    return new Rectangle(left, top, right - left, bottom - top);
                }
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct tagSIZE
            {
                int cx, cy;

                public tagSIZE(Size size)
                {
                    cx = size.Width;
                    cy = size.Height;
                }

                public static tagSIZE FromSize(Size size)
                {
                    return new tagSIZE(size);
                }

                public Size ToSize()
                {
                    return new Size(cx, cy);
                }
            }

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int SetWindowTheme(IntPtr hwnd,
                [MarshalAs(UnmanagedType.LPWStr)] string pszSubAppName,
                [MarshalAs(UnmanagedType.LPWStr)] string pszSubIdList);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int SetWindowTheme(IntPtr hwnd,
                [MarshalAs(UnmanagedType.LPWStr)] string pszSubAppName,
                IntPtr pszSubIdList);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr OpenThemeData(IntPtr hwnd,
                [MarshalAs(UnmanagedType.LPWStr)] string pszClassList);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int CloseThemeData(IntPtr hTheme);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int DrawThemeBackground(IntPtr hTheme,
                IntPtr hdc,
                int iPartId,
                int iStateId,
                ref RECT pRect,
                ref RECT pClipRect);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int DrawThemeText(IntPtr hTheme,
                IntPtr hdc,
                int iPartId,
                int iStateId,
                [MarshalAs(UnmanagedType.LPWStr)] string pszText,
                int iCharCount,
                uint dwTextFlags,
                uint dwTextFlags2,
                ref RECT pRect);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int GetThemeTextExtent(IntPtr hTheme,
                IntPtr hdc,
                int iPartId,
                int iStateId,
                [MarshalAs(UnmanagedType.LPWStr)] string pszText,
                int iCharCount,
                uint dwTextFlags,
                ref RECT pBoundingRect,
                out RECT pExtentRect);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int GetThemeTextExtent(IntPtr hTheme,
                IntPtr hdc,
                int iPartId,
                int iStateId,
                [MarshalAs(UnmanagedType.LPWStr)] string pszText,
                int iCharCount,
                uint dwTextFlags,
                IntPtr pBoundingRect,
                out RECT pExtentRect);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int GetThemePartSize(IntPtr hTheme,
                IntPtr hdc,
                int iPartId,
                int iStateId,
                ref RECT prc,
                uint eSize,
                out tagSIZE psz);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            public static extern int GetThemePartSize(IntPtr hTheme,
                IntPtr hdc,
                int iPartId,
                int iStateId,
                IntPtr prc,
                uint eSize,
                out tagSIZE psz);

            [DllImport("Uxtheme.dll", CharSet = CharSet.Unicode)]
            [return:MarshalAs(UnmanagedType.Bool)]
            public static extern bool IsThemePartDefined(IntPtr hTheme,
                int iPartId,
                int iStateId);
        }

        // Public classes ----------------------------------------------------

        public class Context
        {
            internal IntPtr themeHandle;

            public Context(IntPtr newThemeHandle)
            {
                themeHandle = newThemeHandle;
            }
        }

        [Flags]
        public enum TextFlags : uint
        {
            Top                  = 0x00000000,
            Left                 = 0x00000000,
            Center               = 0x00000001,
            Right                = 0x00000002,
            Vcenter              = 0x00000004,
            Bottom               = 0x00000008,
            WordBreak            = 0x00000010,
            SingleLine           = 0x00000020,
            ExpandTabs           = 0x00000040,
            TabStop              = 0x00000080,
            NoClip               = 0x00000100,
            ExternalLeading      = 0x00000200,
            CalcRect             = 0x00000400,
            NoPrefix             = 0x00000800,
            Internal             = 0x00001000,
            EditControl          = 0x00002000,
            PathEllipsis         = 0x00004000,
            EndEllipsis          = 0x00008000,
            Modifystring         = 0x00010000,
            RtlReading           = 0x00020000,
            WordEllipsis         = 0x00040000,
            NoFullWidthCharBreak = 0x00080000,
            HidePrefix           = 0x00100000,
            PrefixOnly           = 0x00200000
        }

        public enum ThemeSize : uint
        {
            // Minimum size
            TS_MIN = 0,
            // Size, which will best fit available space
            TS_TRUE = 1,
            // Size, that theme manager uses to draw a part
            TS_DRAW = 2
        }

        // Public methods ----------------------------------------------------

        public static void SetTheme(Control control, string theme)
        {
            control.CheckNull("control");
            Native.SetWindowTheme(control.Handle, theme, null);
        }

        public static Context BeginWork(Control control, string classList)
        {
            IntPtr handle = Native.OpenThemeData(control.Handle, classList);
            if (handle == IntPtr.Zero)
                throw new InvalidOperationException("Internal error: cannot create UXTheme context!");

            return new Context(handle);
        }

        public static void EndWork(Context context)
        {
            Native.CloseThemeData(context.themeHandle);
        }

        public static void DrawBackground(Context context,
            IntPtr hdc,
            int partId,
            int stateId,
            Rectangle rect,
            Rectangle clipRect)
        {
            Native.RECT nativeRect = Native.RECT.FromRectangle(rect);
            Native.RECT nativeClipRect = Native.RECT.FromRectangle(clipRect);
            Native.DrawThemeBackground(context.themeHandle, hdc, partId, stateId, ref nativeRect, ref nativeClipRect);
        }

        public static void DrawBackground(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            Rectangle rect,
            Rectangle clipRect)
        {
            IntPtr hdc = graphics.GetHdc();
            try
            {
                DrawBackground(context, hdc, partId, stateId, rect, clipRect);
            }
            finally
            {
                graphics.ReleaseHdc(hdc);
            }
        }

        public static void DrawBackground(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            Rectangle rect)
        {
            DrawBackground(context, graphics, partId, stateId, rect, rect);
        }

        public static void DrawText(Context context,
            IntPtr hdc,
            int partId,
            int stateId,
            string text,
            TextFlags flags,
            ref Rectangle rect)
        {
            Native.RECT nativeRect = Native.RECT.FromRectangle(rect);
            Native.DrawThemeText(context.themeHandle,
                hdc,
                partId,
                stateId,
                text,
                text.Length,
                (uint)flags,
                0,
                ref nativeRect);
            rect = nativeRect.ToRectangle();
        }

        public static void DrawText(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            string text,
            TextFlags flags,
            ref Rectangle rect)
        {
            IntPtr hdc = graphics.GetHdc();
            try
            {
                DrawText(context, hdc, partId, stateId, text, flags, ref rect);
            }
            finally
            {
                graphics.ReleaseHdc(hdc);
            }
        }

        public static void GetTextExtent(Context context,
            IntPtr hdc,
            int partId,
            int stateId,
            string text,
            TextFlags flags,
            ref Rectangle boundingRect,
            out Rectangle extentRect)
        {
            Native.RECT nativeRect = Native.RECT.FromRectangle(boundingRect);
            Native.RECT nativeExtentRect;

            Native.GetThemeTextExtent(context.themeHandle,
                hdc,
                partId,
                stateId,
                text,
                -1, //text.Length,
                (uint)flags,
                ref nativeRect,
                out nativeExtentRect);

            boundingRect = nativeRect.ToRectangle();
            extentRect = nativeExtentRect.ToRectangle();
        }

        public static void GetTextExtent(Context context,
            IntPtr hdc,
            int partId,
            int stateId,
            string text,
            TextFlags flags,
            out Rectangle extentRect)
        {
            Native.RECT nativeExtentRect;

            Native.GetThemeTextExtent(context.themeHandle,
                hdc,
                partId,
                stateId,
                text,
                -1, //text.Length,
                (uint)flags,
                IntPtr.Zero,
                out nativeExtentRect);

            extentRect = nativeExtentRect.ToRectangle();
        }

        public static void GetTextExtent(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            string text,
            TextFlags flags,
            ref Rectangle boundingRect,
            out Rectangle extentRect)
        {
            IntPtr hdc = graphics.GetHdc();
            try
            {
                GetTextExtent(context, hdc, partId, stateId, text, flags, ref boundingRect, out extentRect);
            }
            finally
            {
                graphics.ReleaseHdc(hdc);
            }
        }

        public static void GetTextExtent(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            string text,
            TextFlags flags,
            out Rectangle extentRect)
        {
            IntPtr hdc = graphics.GetHdc();
            try
            {
                GetTextExtent(context, hdc, partId, stateId, text, flags, out extentRect);
            }
            finally
            {
                graphics.ReleaseHdc(hdc);
            }
        }

        public static void GetPartSize(Context context,
            IntPtr hdc,
            int partId,
            int stateId,
            ref Rectangle rect,
            ThemeSize themeSize,
            out Size partSize)
        {
            Native.tagSIZE nativePartSize;
            Native.RECT nativeRect = Native.RECT.FromRectangle(rect);

            Native.GetThemePartSize(context.themeHandle,
                hdc,
                partId,
                stateId,
                ref nativeRect,
                (uint)themeSize,
                out nativePartSize);

            rect = nativeRect.ToRectangle();
            partSize = nativePartSize.ToSize();
        }

        public static void GetPartSize(Context context,
            IntPtr hdc,
            int partId,
            int stateId,
            ThemeSize themeSize,
            out Size partSize)
        {
            Native.tagSIZE nativePartSize;

            Native.GetThemePartSize(context.themeHandle,
                hdc,
                partId,
                stateId,
                IntPtr.Zero,
                (uint)themeSize,
                out nativePartSize);

            partSize = nativePartSize.ToSize();
        }

        public static void GetPartSize(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            ref Rectangle rect,
            ThemeSize themeSize,
            out Size partSize)
        {
            IntPtr hdc = graphics.GetHdc();
            try
            {
                GetPartSize(context, hdc, partId, stateId, ref rect, themeSize, out partSize);
            }
            finally
            {
                graphics.ReleaseHdc(hdc);
            }
        }

        public static void GetPartSize(Context context,
            Graphics graphics,
            int partId,
            int stateId,
            ThemeSize themeSize,
            out Size partSize)
        {
            IntPtr hdc = graphics.GetHdc();
            try
            {
                GetPartSize(context, hdc, partId, stateId, themeSize, out partSize);
            }
            finally
            {
                graphics.ReleaseHdc(hdc);
            }
        }

        public static bool IsThemePartDefined(Context context,
            int partId)
        {
            return Native.IsThemePartDefined(context.themeHandle, partId, 0);
        }
    }
}
